package org.bankabeule.core.parsers;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.StringReader;

import org.apache.xerces.parsers.DOMParser;
import org.bankabeule.Account;
import org.bankabeule.AccountTypeEnum;
import org.bankabeule.BankabeuleFactory;
import org.bankabeule.Date;
import org.bankabeule.Model;
import org.bankabeule.MoneyEnum;
import org.bankabeule.Operation;
import org.bankabeule.OperationTypeEnum;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

/**
 * @author sdudoit
 */
public final class OFX1Parser {

	/**
	 * XML Parser
	 */
	private DOMParser parser;
	
	/**
	 * XML Document
	 */
	private Document document;
	
	/**
	 * Constructor
	 */
	public OFX1Parser() {
		parser = new DOMParser();
	}

	/**
	 * Parse
	 * 
	 * @throws Exception
	 */
	public void parse(String ofxFilePath) throws Exception {
		// Convert OFX to XML
		String xmlFromOfx = getXMLFromOFX(ofxFilePath);
		File tmp = new File("Test.xml");
		DataOutputStream out = new DataOutputStream(new FileOutputStream(tmp));
		out.writeBytes(xmlFromOfx);
		// Parse XML
		parser.parse(new InputSource(new StringReader(xmlFromOfx)));
		document = parser.getDocument();
	}
	
	
	/**
	 * Get model
	 * 
	 * @throws Exception
	 */
	public Model getModel() throws Exception {
		// Build a new model
		Model model = BankabeuleFactory.eINSTANCE.createModel();	
		
		// Get all accounts
		NodeList list =	document.getElementsByTagName("STMTTRNRS");
		for (int i=0; i<list.getLength(); i++) {
			model.getAccounts().add(getAccount(list.item(i)));
		}
		
		// return model found
		return model;
	}
	
	
	
	/**
	 * Get account from node
	 * 
	 * @throws Exception
	 */
	private Account getAccount(Node node) throws Exception {
		// Build a new account
		Account account = BankabeuleFactory.eINSTANCE.createAccount();
		
		// Look at children
		NodeList children = node.getChildNodes();
		for (int i=0; i<children.getLength(); i++) {
			Node item = children.item(i);
			if (item.getNodeName().equals("TRNUID")) {
				account.setId(item.getTextContent());
			} else if (item.getNodeName().equals("STMTRS")) {
				// Look at children
				NodeList children2 = item.getChildNodes();
				for (int i2=0; i2<children2.getLength(); i2++) {
					Node item2 = children2.item(i2);
					if (item2.getNodeName().equals("CURDEF")) {
						account.setMoney(MoneyEnum.get(item2.getTextContent()));
					} else if (item2.getNodeName().equals("BANKACCTFROM")) {
						// Look at children
						NodeList children3 = item2.getChildNodes();
						for (int i3=0; i3<children3.getLength(); i3++) {
							Node item3 = children3.item(i3);
							if (item3.getNodeName().equals("BANKID")) {	
								account.setBankid(item3.getTextContent());
							} else if (item3.getNodeName().equals("BRANCHID")) {
								account.setBranchid(item3.getTextContent());
							} else if (item3.getNodeName().equals("ACCTID")) {
								account.setId(item3.getTextContent());
							} else if (item3.getNodeName().equals("ACCTTYPE")) {
								account.setType(AccountTypeEnum.get(item3.getTextContent()));
							}
						}
					} else if (item2.getNodeName().equals("BANKTRANLIST")) {
						// Look at children
						NodeList children3 = item2.getChildNodes();
						for (int i3=0; i3<children3.getLength(); i3++) {
							Node item3 = children3.item(i3);
							if (item3.getNodeName().equals("STMTTRN")) {
								account.getOperations().add(getOperation(item3));
							} 
						}
					} else if (item2.getNodeName().equals("AVAILBAL")) {
						// Look at children
						NodeList children3 = item2.getChildNodes();
						for (int i3=0; i3<children3.getLength(); i3++) {
							Node item3 = children3.item(i3);
							if (item3.getNodeName().equals("BALAMT")) {
								account.setValue(Double.parseDouble(item3.getTextContent()));
							} 
							/*else if (item3.getNodeName().equals("DTASOF")) {
								Date date = BankabeuleFactory.eINSTANCE.createDate();
								String s = item3.getTextContent();
								date.setYear(Integer.parseInt(s.substring(0, 4)));
								date.setMonth(Integer.parseInt(s.substring(4, 6)));
								date.setDay(Integer.parseInt(s.substring(6, 8)));
								account.setDate(date);
							}*/
						}
					}
				}					
			}
		}

		// return result
		return account;		
	}
	
	
	
	/**
	 * Get operation
	 * 
	 * @throws Exception
	 */
	private Operation getOperation(Node node) throws Exception {
		// Build a new operation
		Operation operation = BankabeuleFactory.eINSTANCE.createOperation();
		
		// Look at children
		NodeList children = node.getChildNodes();
		for (int i=0; i<children.getLength(); i++) {
			Node item = children.item(i);
			if (item.getNodeName().equals("TRNTYPE")) {
				operation.setType(OperationTypeEnum.get(item.getTextContent()));
			} else if (item.getNodeName().equals("DTPOSTED")) {
				Date date = BankabeuleFactory.eINSTANCE.createDate();
				String s = item.getTextContent();
				date.setYear(Integer.parseInt(s.substring(0, 4)));
				date.setMonth(Integer.parseInt(s.substring(4, 6)));
				date.setDay(Integer.parseInt(s.substring(6, 8)));
				operation.setDate(date);
			} else if (item.getNodeName().equals("TRNAMT")) {
				operation.setValue(Double.parseDouble(item.getTextContent()));
			} else if (item.getNodeName().equals("FITID")) {
				operation.setId(item.getTextContent());				
			} else if (item.getNodeName().equals("NAME")) {
				operation.setName(item.getTextContent());
			} else if (item.getNodeName().equals("MEMO")) {
				operation.setDescription(item.getTextContent());
			}
		}
		
		// return results
		return operation;
	}	
	
	

	/**
	 * Get XML content from OFX file
	 */
	@SuppressWarnings("deprecation")
	public String getXMLFromOFX(String ofxFilePath) throws Exception {
		DataInputStream dIn = new DataInputStream(new FileInputStream(
				ofxFilePath));
		String line = dIn.readLine();
		StringBuffer buffer = new StringBuffer();
		// Get header
		buffer.append("<?xml version=\"1.0\"?>\n");
		buffer.append("<?OFX ");
		while (line.indexOf("<") != 0) {
			if (line.length() > 0) {
				buffer.append(line.replaceAll(":", "=\"") + "\" ");
			}
			line = dIn.readLine();
		}
		buffer.append("?>\n");
		// Get content
		String m_ofx = "";
		while (line != null) {
			m_ofx += line + "\n";
			line = dIn.readLine();
		}
		dIn.close();
		// Change content to XML
		boolean addCloseTag;
		int tag2Start;
		int tagStart;
		int tagEnd;
		String tag;
		line = "";
		while (m_ofx != "") {
			addCloseTag = false;
			tagStart = m_ofx.indexOf("<");
			if (tagStart == -1) {
				break;
			}
			tagEnd = m_ofx.indexOf(">");
			if (tagEnd <= tagStart + 1) {
				throw new IOException("PARSE ERROR: Invalid tag");
			}
			tag = m_ofx.substring(tagStart + 1, tagEnd);
			if (tag.indexOf(" ") != -1) {
				throw new IOException("PARSE ERROR: Invalid tag");
			}
			if (!tag.startsWith("/")) {
				addCloseTag = (m_ofx.indexOf("</" + tag + ">") == -1);
			}
			tag2Start = m_ofx.indexOf("<", tagEnd);
			if (m_ofx.indexOf("\n", tagEnd) < tag2Start) {
				tag2Start = m_ofx.indexOf("\n", tagEnd);
			}
			if (tag2Start == -1) {
				tag2Start = m_ofx.length();
			}

			String data = m_ofx.substring(tagEnd + 1, tag2Start);
			line = m_ofx.substring(0, tagEnd + 1) + xmlEncodeTextAsPCDATA(data);

			m_ofx = m_ofx.substring(tag2Start);
			if (addCloseTag) {
				line += "</" + tag + ">";
			}
			buffer.append(line);
		}
		buffer.append(m_ofx);
		return buffer.toString();
	}

	
	/**
	 * Encodes strings for XML
	 */
	private String xmlEncodeTextAsPCDATA(String text) {
		if (text == null)
			return null;
		char c;
		StringBuffer n = new StringBuffer(text.length() * 2);
		for (int i = 0; i < text.length(); i++) {
			c = text.charAt(i);
			switch (c) {
			case '&':
				n.append("&");
				break;
			case '<':
				n.append("<");
				break;
			case '>':
				n.append(">");
				break;
			case '"':
				n.append("\"");
				break;
			case '\'':
				n.append("'");
				break;
			default: {
				n.append(c);
				break;
			}
			}
		}

		return n.toString();
	}

}